package org.zjvis.datascience.service.csv.slice;

import lombok.Data;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.zjvis.datascience.common.exception.BaseErrorCode;
import org.zjvis.datascience.common.exception.DataScienceException;
import org.zjvis.datascience.service.csv.CsvSemiotic;
import org.zjvis.datascience.service.csv.ErrorLineCode;
import org.zjvis.datascience.service.csv.semiotic.Escape;
import org.zjvis.datascience.service.csv.semiotic.Quote;
import org.zjvis.datascience.service.csv.semiotic.Separate;
import org.zjvis.datascience.service.csv.State;
import org.zjvis.datascience.service.csv.dto.ErrorInfo;
import org.zjvis.datascience.service.csv.dto.ErrorLineCodeInfo;

import java.io.BufferedReader;
import java.io.IOException;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import static org.zjvis.datascience.common.constant.DatasetConstant.utf8IllegalChar;

/**
 * @description Csv文件解析 读取类
 * @date 2021-12-29
 */
@Data
public class CsvReader {
    private final static Logger logger = LoggerFactory.getLogger("CsvReader");
    //    "[^" +
//        "\\u0020-\\u9FA5" +
//        "\\uFF00-\\uFFEF" + // 半型及全型形式 (Halfwidth and Fullwidth Form)
//        "\\uAC00-\\uD7AF]"; // 朝鲜文音节
    private static final String delimiter = "\r\n"; // 一行的结尾即换行
    private String separate = ",";  //
    private String quote = "\"";
    private String escape = "\"";
    private Map<ErrorLineCode, List<String>> errorLineInfoMap = null;
    private Map<ErrorLineCode, Integer> errorLineCnt = null;
    private Integer MAXErrorShow = 100;
    private final BufferedReader reader;
    // 解析状态,根据状态转换解析结果
    private State state;
    private String errorLine = "";
    public State getState() {
        return this.state;
    }
    private final String UTF8_BOM = "\uFEFF";
    private List<String> headers = null;
    private int headerSize;
    private int nowCnt = 0;
    private int totalCnt = 0;
    private int currentChunk = 0;
    private int totalChunk = 0;
    private String beginRow = "";
    private String endRow = "";
    private String[] text = null;
    private int idx = -1;
    private boolean endWithN;

    /**
     * 初始化
     * @param reader 源文件
     */

    public CsvReader(BufferedReader reader, boolean firstLineisTitle, CsvSemiotic csvSemiotic, int totalCnt, int currentChunk, int totalChunk, boolean endWithN, int maxShow) {
        if (csvSemiotic.getEscape() != Escape.NULL) escape = csvSemiotic.getEscape().getSymbol();
        else escape = csvSemiotic.getOtherEscape();
        if (csvSemiotic.getSeparate() != Separate.NULL) separate = csvSemiotic.getSeparate().getSymbol();
        else separate = csvSemiotic.getOtherSeparate();
        if (csvSemiotic.getQuote() != Quote.NULL) quote = csvSemiotic.getQuote().getSymbol();
        else quote = csvSemiotic.getOtherQuote();
        this.MAXErrorShow = maxShow;
        this.reader = reader;
        this.errorLineInfoMap = new HashMap<>();
        this.errorLineCnt = new HashMap<>();
        this.state = State.newStart;
        this.totalCnt = totalCnt;
        this.nowCnt = 0;
        this.currentChunk = currentChunk;
        this.totalChunk = totalChunk;
        this.endWithN = endWithN;
        if (firstLineisTitle) {
            headers = readNext(true);
            if (headers != null)
                this.headerSize = headers.size();
        }
    }

    /**
     * 初始化
     * @param reader 源文件
     */

    public CsvReader(BufferedReader reader, boolean firstLineisTitle, CsvSemiotic csvSemiotic) {
        if (csvSemiotic.getEscape() != Escape.NULL) escape = csvSemiotic.getEscape().getSymbol();
        else escape = csvSemiotic.getOtherEscape();
        if (csvSemiotic.getSeparate() != Separate.NULL) separate = csvSemiotic.getSeparate().getSymbol();
        else separate = csvSemiotic.getOtherSeparate();
        if (csvSemiotic.getQuote() != Quote.NULL) quote = csvSemiotic.getQuote().getSymbol();
        else quote = csvSemiotic.getOtherQuote();
        this.MAXErrorShow = 100;
        this.reader = reader;
        this.errorLineInfoMap = new HashMap<>();
        this.errorLineCnt = new HashMap<>();
        this.state = State.newStart;
        if (firstLineisTitle) {
            headers = readNext(true);
            if (headers != null)
                this.headerSize = headers.size();
        }
    }

    /**
     * 初始化
     * @param reader 源文件
     */

    public CsvReader(BufferedReader reader, boolean firstLineisTitle, CsvSemiotic csvSemiotic, int totalCnt, int currentChunk, int totalChunk, boolean endWithN, int headerSize, int maxShow) {
        if (csvSemiotic.getEscape() != Escape.NULL) escape = csvSemiotic.getEscape().getSymbol();
        else escape = csvSemiotic.getOtherEscape();
        if (csvSemiotic.getSeparate() != Separate.NULL) separate = csvSemiotic.getSeparate().getSymbol();
        else separate = csvSemiotic.getOtherSeparate();
        if (csvSemiotic.getQuote() != Quote.NULL) quote = csvSemiotic.getQuote().getSymbol();
        else quote = csvSemiotic.getOtherQuote();
        this.MAXErrorShow = maxShow;
        this.reader = reader;
        this.errorLineInfoMap = new HashMap<>();
        this.errorLineCnt = new HashMap<>();
        this.state = State.newStart;
        this.headerSize = headerSize;
        this.totalCnt = totalCnt;
        this.nowCnt = 0;
        this.currentChunk = currentChunk;
        this.totalChunk = totalChunk;
        this.endWithN = endWithN;
        if (firstLineisTitle) {
            headers = readNext(true);
        }
    }


    /**
     * 初始化
     */

    public CsvReader(String[] text, CsvSemiotic csvSemiotic, int headerSize) {
        if (csvSemiotic.getEscape() != Escape.NULL) escape = csvSemiotic.getEscape().getSymbol();
        else escape = csvSemiotic.getOtherEscape();
        if (csvSemiotic.getSeparate() != Separate.NULL) separate = csvSemiotic.getSeparate().getSymbol();
        else separate = csvSemiotic.getOtherSeparate();
        if (csvSemiotic.getQuote() != Quote.NULL) quote = csvSemiotic.getQuote().getSymbol();
        else quote = csvSemiotic.getOtherQuote();
        this.MAXErrorShow = 100;
        this.text = text;
        this.errorLineInfoMap = new HashMap<>();
        this.errorLineCnt = new HashMap<>();
        this.state = State.newStart;
        this.headerSize = headerSize;
        // 初始化指针
        idx = 0;
        reader = null;
    }

    public List<String> getHeaders(boolean needHeader) throws DataScienceException {
        if (needHeader) {
            if (headers == null) {
                logger.warn("用户上传不合规文件，造成预览失败，标题解析错误结果为null，可能存在非法字符，考虑文件编码错误");
                throw DataScienceException.of(BaseErrorCode.DATASET_CSV_PREVIEW_ERROR, null,
                        "文件预览失败，标题存在非法字符或格式错误，考虑文件编码错误");
            }
            Pattern pattern = Pattern.compile(utf8IllegalChar);
            for (String header : headers) {
                if (header == null) continue;
                Matcher matcher = pattern.matcher(header);
                if (matcher.find()) {
                    logger.warn("用户上传不合规文件，造成预览失败，标题存在非法字符，考虑文件编码错误");
                    throw DataScienceException.of(BaseErrorCode.DATASET_CSV_PREVIEW_ERROR, null,
                            "文件预览失败，标题存在非法字符，考虑文件编码错误");
                }
            }
        }
        return headers;
    }

    public ErrorInfo geterrorLineInfo() {
        Set<Map.Entry<ErrorLineCode, List<String>>> entries = errorLineInfoMap.entrySet();
        Map<ErrorLineCode, ErrorLineCodeInfo> errorLineCodeInfoMap = new HashMap<>();
        int count = 0;
        for (Map.Entry<ErrorLineCode, List<String>> entry : entries) {
            ErrorLineCode key = entry.getKey();
            ErrorLineCodeInfo errorLineCodeInfo = new ErrorLineCodeInfo();
            List<String> value = entry.getValue();
            errorLineCodeInfo.setCount(errorLineCnt.get(key));
            errorLineCodeInfo.setLines(value);
            errorLineCodeInfoMap.put(key, errorLineCodeInfo);
            count += errorLineCnt.get(key);
        }
        return ErrorInfo.builder().errorCount(count).errorLineCodeInfoMap(errorLineCodeInfoMap).build();
    }

    private String readLine() {
        if (idx == text.length) return null;
        return text[idx++];
    }

    public List<String> readText() {
        // 完整一行
        String line;
        // 初始化状态
        state = State.newStart;
        List<String> result = new ArrayList<>();
        StringBuilder stringBuilder = new StringBuilder();
        List<String> list = null;
        int cnt = 0;
        errorLine = "";
        try {
            // 循环直到解析正确的一行
            while (true) {
                while ((line = readLine()) != null) {
                    if (line.startsWith(UTF8_BOM)) {
                        line = line.substring(1);
                    }
                    errorLine += line + '\n';
                    char[] chars = line.toCharArray();
                    Pattern pattern = Pattern.compile(utf8IllegalChar);
                    Matcher matcher = pattern.matcher(line);
                    // 对于非法字符的检测
                    if (matcher.find()) {
                        list = errorLineInfoMap.getOrDefault(ErrorLineCode.ContainIllegalChar, new ArrayList<>());
                        cnt = errorLineCnt.getOrDefault(ErrorLineCode.ContainIllegalChar, 0);
                        if (cnt < MAXErrorShow) {
                            list.add(errorLine);
                            errorLineInfoMap.put(ErrorLineCode.ContainIllegalChar, list);
                        }
                        cnt++;
                        errorLineCnt.put(ErrorLineCode.ContainIllegalChar, cnt);
                        state = State.fail;
                        result = null;
                        return result;
                    }
                    // 解析每个字符
                    for (char aChar : chars) {
                        if (separate.equals(aChar + "")) {
                            // 字段分隔符,如 ,
                            switch (state) {
                                case newStart:
                                    // 空字段
                                    result.add(stringBuilder.toString());
                                    stringBuilder = new StringBuilder();
                                    state = State.endfield; /// 转换为字段结束状态
                                    break;
                                case quote:
                                    // 引号状态遇到
                                    stringBuilder.append(aChar);
                                    break;
                                case escape:
                                    // 转义状态遇到，包含
                                    stringBuilder.append(this.escape + this.escape + aChar);
                                    break;
                                case judgequote:
                                    result.add(this.quote + stringBuilder.toString() + this.quote);
                                    stringBuilder = new StringBuilder();
                                    state = State.endfield;
                                    break;
                                case endfield:
                                    // 空字段
                                    result.add("");
                                    break;
                                case endrow:
                                case enddocument:
                                    state = State.newStart;
                                    if (result.size() > headerSize) {
                                        addErrorLine(ErrorLineCode.ExceedColNum, errorLine, false);
                                        result = null;
                                    } else if (result.size() < headerSize) {
                                        addErrorLine(ErrorLineCode.UnderColNum, errorLine, false);
                                        result = null;
                                    }
                                    return result;
                                case fail:
                                    state = State.newStart;
                                    result = null;
                                    return result;
                            }
                        } else if (quote.equals(aChar + "")) {
                            // 字符串符号
                            switch (state) {
                                case newStart:
                                    if (stringBuilder.length()!=0) {
                                        addErrorLine(ErrorLineCode.EscapeWithNoQuote, errorLine, false);
                                    } else {
                                        state = State.quote;
                                    }
                                    break;
                                case quote:
                                    state = State.judgequote;
                                    break;
                                case escape:
                                    stringBuilder.append(escape + quote);
                                    state = State.quote;
                                    break;
                                case judgequote:
                                    if (quote.equals(escape)) {
                                        stringBuilder.append(escape + quote);
                                        state = State.quote;
                                    } else {
                                        addErrorLine(ErrorLineCode.QuoteWithNoEscape, errorLine, false);
                                    }
                                    break;
                                case endfield:
                                    state = State.quote;
                                    break;
                                case endrow:
                                case enddocument:
                                    if (result.size() > headerSize) {
                                        addErrorLine(ErrorLineCode.ExceedColNum, errorLine, false);
                                        result = null;
                                    } else if (result.size() < headerSize) {
                                        addErrorLine(ErrorLineCode.UnderColNum, errorLine, false);
                                        result = null;
                                    }
                                    return result;
                                case fail:
                                    result = null;
                                    return null;
                            }
                        } else if (escape.equals(aChar + "")) {
                            // 说明escape不等于quote
                            switch (state) {
                                case newStart:
                                    addErrorLine(ErrorLineCode.EscapeWithNoQuote, errorLine, false);
                                    break;
                                case quote:
                                    state = State.escape;
                                    break;
                                case escape:
                                    stringBuilder.append(escape + escape);
                                    state = State.quote;
                                    break;
                                case judgequote:
                                    addErrorLine(ErrorLineCode.QuoteWithNoEscape, errorLine, false);
                                    result = null;
                                    break;
                                case endfield:
                                    addErrorLine(ErrorLineCode.EscapeWithNoQuote, errorLine, false);
                                    result = null;
                                    break;
                                case endrow:
                                case enddocument:
                                    if (result.size() > headerSize) {
                                        addErrorLine(ErrorLineCode.ExceedColNum, errorLine, false);
                                        result = null;
                                    } else if (result.size() < headerSize) {
                                        addErrorLine(ErrorLineCode.UnderColNum, errorLine, false);
                                        result = null;
                                    }
                                    return result;
                                case fail:
                                    result = null;
                                    return null;
                            }
                        } else {
                            if (state == State.fail) {
                                result = null;
                                return null;
                            }
                            if (state == State.endfield) state = State.newStart;
                            if (state == State.judgequote) {
                                addErrorLine(ErrorLineCode.QuoteWithNoEscape, errorLine, false);
                                result = null;
//                                }
                            } else if (state == State.escape) {
                                stringBuilder.append(escape + aChar);
                                state = State.quote;
                            }
                            else stringBuilder.append(aChar);
                        }
                    }
                    // 行分隔符
                    switch (state) {
                        case newStart:
                            state = State.endrow;
                            break;
                        case quote:
                            stringBuilder.append(delimiter);
                            break;
                        case escape:
                            stringBuilder.append(escape + delimiter);
                            break;
                        case judgequote:
                            result.add(quote + stringBuilder.toString() + quote);
                            stringBuilder = new StringBuilder();
                            state = State.endrow;
                            break;
                        case endfield:
                            // 空字段
                            result.add("");
                            state = State.endrow;
                            break;
                        case endrow:
                        case enddocument:
                            if (result.size() > headerSize) {
                                addErrorLine(ErrorLineCode.ExceedColNum, errorLine, false);
                                result = null;
                            } else if (result.size() < headerSize) {
                                addErrorLine(ErrorLineCode.UnderColNum, errorLine, false);
                                result = null;
                            }
                            return result;
                        case fail:
                            result = null;
                            return null;
                    }
                    if (state == State.fail) {
                        result = null;
                        return null;
                    }
                    if (state == State.endrow || state == State.enddocument) {
                        if (stringBuilder.length() != 0) result.add(stringBuilder.toString());
                        if (result.size() > headerSize) {
                            addErrorLine(ErrorLineCode.ExceedColNum, errorLine, false);
                            result = null;
                        } else if (result.size() < headerSize) {
                            addErrorLine(ErrorLineCode.UnderColNum, errorLine, false);
                            result = null;
                        }
                        return result;
                    }
                }
                switch (state) {
                    case newStart:
                    case fail:
                        result = null;
                        break;
                    case quote:
                        result = null;
//                        result.add(this.quote + stringBuilder.toString());
                        addErrorLine(ErrorLineCode.EscapeWithNoQuote, errorLine, false);
                        break;
                    case escape:
                        result.add(stringBuilder.toString() + escape);
                        break;
                    case judgequote:
                        result.add(stringBuilder.toString() + this.quote);
                        break;
                    case endfield:
                    case endrow:
                    case enddocument:
                        break;
                }
                state = State.enddocument;
                break;
            }
        } finally {
            if (result!=null && result.size() > headerSize) {
                addErrorLine(ErrorLineCode.ExceedColNum, errorLine, false);
                result = null;
            } else if (result!=null && result.size() < headerSize) {
                addErrorLine(ErrorLineCode.UnderColNum, errorLine, false);
                result = null;
            }
            return result;
        }
    }

    /**
     * 解析
     * 8种状态
     * 4种符号
     * @return
     */
    public List<String> readNext(boolean isFirstLine) {
        // 完整一行
        String line;
        // 初始化状态
        state = State.newStart;
        List<String> result = new ArrayList<>();
        StringBuilder stringBuilder = new StringBuilder();
        List<String> list = null;
        int cnt = 0;
        errorLine = "";
        boolean isBegin = false;
        boolean isEnd = false;
        try {
            // 循环直到解析正确的一行
            while (true) {
                while ((line = reader.readLine()) != null) {
                    if (line.startsWith(UTF8_BOM)) {
                        line = line.substring(1);
                    }
                    nowCnt++; // 当前行号
                    if (totalChunk > 1) {
                        if (currentChunk != totalChunk - 1 && nowCnt == totalCnt) {
                            // 除了最后一块最后一行不用理会需要解析，其他也存入
                            if (endWithN) endRow = errorLine + line + '\n';
                            else endRow = errorLine + line;
                            state = State.enddocument;
                            // 直接返回
                            isEnd = true;
                            return null;
                        }
                        if (currentChunk != 0 && nowCnt == 1) {
                            // 先解析找到真正的第一行再不返回，存入begin，所以需要先标记
                            isBegin = true;
                        }
                    }
                    errorLine += line + '\n';
                    char[] chars = line.toCharArray();
                    Pattern pattern = Pattern.compile(utf8IllegalChar);
                    Matcher matcher = pattern.matcher(line);
                    // 对于非法字符的检测
                    if (!isFirstLine && matcher.find()) {
                        list = errorLineInfoMap.getOrDefault(ErrorLineCode.ContainIllegalChar, new ArrayList<>());
                        cnt = errorLineCnt.getOrDefault(ErrorLineCode.ContainIllegalChar, 0);
                        if (cnt < MAXErrorShow) {
                            list.add(errorLine);
                            errorLineInfoMap.put(ErrorLineCode.ContainIllegalChar, list);
                        }
                        cnt++;
                        errorLineCnt.put(ErrorLineCode.ContainIllegalChar, cnt);
                        state = State.fail;
                        result = null;
                        return result;
                    }
                    // 解析每个字符
                    for (char aChar : chars) {
                        if (separate.equals(aChar + "")) {
                            // 字段分隔符,如 ,
                            switch (state) {
                                case newStart:
                                    // 空字段
                                    result.add(stringBuilder.toString());
                                    stringBuilder = new StringBuilder();
                                    state = State.endfield; /// 转换为字段结束状态
                                    break;
                                case quote:
                                    // 引号状态遇到
                                    stringBuilder.append(aChar);
                                    break;
                                case escape:
                                    // 转义状态遇到，包含
                                    stringBuilder.append(this.escape + this.escape + aChar);
                                    break;
                                case judgequote:
                                    result.add(this.quote + stringBuilder.toString() + this.quote);
                                    stringBuilder = new StringBuilder();
                                    state = State.endfield;
                                    break;
                                case endfield:
                                    // 空字段
                                    result.add("");
                                    break;
                                case endrow:
                                case enddocument:
                                    state = State.newStart;
                                    if (isFirstLine) return result;
                                    if (result.size() > headerSize) {
                                        addErrorLine(ErrorLineCode.ExceedColNum, errorLine, isBegin);
                                        result = null;
                                    } else if (result.size() < headerSize) {
                                        addErrorLine(ErrorLineCode.UnderColNum, errorLine, isBegin);
                                        result = null;
                                    }
                                    return result;
                                case fail:
                                    state = State.newStart;
                                    result = null;
                                    return result;
                            }
                        } else if (quote.equals(aChar + "")) {
                            // 字符串符号
                            switch (state) {
                                case newStart:
                                    if (stringBuilder.length()!=0) {
                                        addErrorLine(ErrorLineCode.EscapeWithNoQuote, errorLine, isBegin);
                                    } else {
                                        state = State.quote;
                                    }
                                    break;
                                case quote:
                                    state = State.judgequote;
                                    break;
                                case escape:
                                    stringBuilder.append(escape + quote);
                                    state = State.quote;
                                    break;
                                case judgequote:
                                    if (quote.equals(escape)) {
                                        stringBuilder.append(escape + quote);
                                        state = State.quote;
                                    } else {
                                        addErrorLine(ErrorLineCode.QuoteWithNoEscape, errorLine, isBegin);
                                    }
                                    break;
                                case endfield:
                                    state = State.quote;
                                    break;
                                case endrow:
                                case enddocument:
                                    if (isFirstLine) return result;
                                    if (result.size() > headerSize) {
                                        addErrorLine(ErrorLineCode.ExceedColNum, errorLine, isBegin);
                                        result = null;
                                    } else if (result.size() < headerSize) {
                                        addErrorLine(ErrorLineCode.UnderColNum, errorLine, isBegin);
                                        result = null;
                                    }
                                    return result;
                                case fail:
                                    result = null;
                                    return null;
                            }
                        } else if (escape.equals(aChar + "")) {
                            // 说明escape不等于quote
                            switch (state) {
                                case newStart:
                                    addErrorLine(ErrorLineCode.EscapeWithNoQuote, errorLine, isBegin);
                                    break;
                                case quote:
                                    state = State.escape;
                                    break;
                                case escape:
                                    stringBuilder.append(escape + escape);
                                    state = State.quote;
                                    break;
                                case judgequote:
                                    addErrorLine(ErrorLineCode.QuoteWithNoEscape, errorLine, isBegin);
                                    result = null;
                                    break;
                                case endfield:
                                    addErrorLine(ErrorLineCode.EscapeWithNoQuote, errorLine, isBegin);
                                    result = null;
                                    break;
                                case endrow:
                                case enddocument:
                                    if (isFirstLine) return result;
                                    if (result.size() > headerSize) {
                                        addErrorLine(ErrorLineCode.ExceedColNum, errorLine, isBegin);
                                        result = null;
                                    } else if (result.size() < headerSize) {
                                        addErrorLine(ErrorLineCode.UnderColNum, errorLine, isBegin);
                                        result = null;
                                    }
                                    return result;
                                case fail:
                                    result = null;
                                    return null;
                            }
                        } else {
                            if (state == State.fail) {
                                result = null;
                                return null;
                            }
                            if (state == State.endfield) state = State.newStart;
                            if (state == State.judgequote) {
                                addErrorLine(ErrorLineCode.QuoteWithNoEscape, errorLine, isBegin);
                                result = null;
//                                }
                            } else if (state == State.escape) {
                                stringBuilder.append(escape + aChar);
                                state = State.quote;
                            }
                            else stringBuilder.append(aChar);
                        }
                    }
                    // 行分隔符
                    switch (state) {
                        case newStart:
                            state = State.endrow;
                            break;
                        case quote:
                            stringBuilder.append(delimiter);
                            break;
                        case escape:
                            stringBuilder.append(escape + delimiter);
                            break;
                        case judgequote:
                            result.add(quote + stringBuilder.toString() + quote);
                            stringBuilder = new StringBuilder();
                            state = State.endrow;
                            break;
                        case endfield:
                            // 空字段
                            result.add("");
                            state = State.endrow;
                            break;
                        case endrow:
                        case enddocument:
                            if (isFirstLine) return result;
                            if (result.size() > headerSize) {
                                addErrorLine(ErrorLineCode.ExceedColNum, errorLine, isBegin);
                                result = null;
                            } else if (result.size() < headerSize) {
                                addErrorLine(ErrorLineCode.UnderColNum, errorLine, isBegin);
                                result = null;
                            }
                            return result;
                        case fail:
                            result = null;
                            return null;
                    }
                    if (state == State.fail) {
                        result = null;
                        return null;
                    }
                    if (state == State.endrow || state == State.enddocument) {
                        if (stringBuilder.length() != 0) result.add(stringBuilder.toString());
                        if (isFirstLine) return result;
                        if (result.size() > headerSize) {
                            addErrorLine(ErrorLineCode.ExceedColNum, errorLine, isBegin);
                            result = null;
                        } else if (result.size() < headerSize) {
                            addErrorLine(ErrorLineCode.UnderColNum, errorLine, isBegin);
                            result = null;
                        }
                        return result;
                    }
                }
                switch (state) {
                    case newStart:
                    case fail:
                        result = null;
                        break;
                    case quote:
                        result = null;
//                        result.add(this.quote + stringBuilder.toString());
                        addErrorLine(ErrorLineCode.EscapeWithNoQuote, errorLine, isBegin);
                        break;
                    case escape:
                        result.add(stringBuilder.toString() + escape);
                        break;
                    case judgequote:
                        result.add(stringBuilder.toString() + this.quote);
                        break;
                    case endfield:
                    case endrow:
                    case enddocument:
                        break;
                }
                state = State.enddocument;
                break;
            }
        } catch (IOException e) {
            logger.error("readNext: " + e);
        } finally {
            if (isEnd) return null;
            if (isBegin) {
                beginRow = errorLine;
                return null;
            }
            if (isFirstLine) return result;
            if (result!=null && result.size() > headerSize) {
                addErrorLine(ErrorLineCode.ExceedColNum, errorLine, isBegin);
                result = null;
            } else if (result!=null && result.size() < headerSize) {
                addErrorLine(ErrorLineCode.UnderColNum, errorLine, isBegin);
                result = null;
            }
            return result;
        }
    }

    public static String getDelimiter() {
        return delimiter;
    }

    public String getSeparate() {
        return separate;
    }

    public void setSeparate(String separate) {
        this.separate = separate;
    }

    public String getQuote() {
        return quote;
    }

    public void setQuote(String quote) {
        this.quote = quote;
    }

    public String getEscape() {
        return escape;
    }

    public void setEscape(String escape) {
        this.escape = escape;
    }

    public void addErrorLine(ErrorLineCode errorLineCode, List<String> result) {
        List<String> list = errorLineInfoMap.getOrDefault(errorLineCode, new ArrayList<>());
        Integer cnt = errorLineCnt.getOrDefault(errorLineCode, 0);
        if (cnt < MAXErrorShow) {
            list.add(String.join(",", result));
            errorLineInfoMap.put(errorLineCode, list);
        }
        cnt++;
        errorLineCnt.put(errorLineCode, cnt);
        state = State.fail;
    }

    public void addErrorLine(ErrorLineCode errorLineCode, String result, boolean isBegin) {
        if (isBegin) {
            state = State.fail;
            return;
        }
        List<String> list = errorLineInfoMap.getOrDefault(errorLineCode, new ArrayList<>());
        Integer cnt = errorLineCnt.getOrDefault(errorLineCode, 0);
        if (cnt < MAXErrorShow) {
            list.add(result);
            errorLineInfoMap.put(errorLineCode, list);
        }
        cnt++;
        errorLineCnt.put(errorLineCode, cnt);
        state = State.fail;
    }
}
